
// 原理：红黑树、回调机制、就绪队列
// 数据就绪后经由回调机制将红黑树的节点放入就绪队列中，无须遍历，效率提高
// 检测是否有事件就绪只须判断就绪队列是否为空即可，时间复杂度O(1)

// 关键1.用户告诉内核：你要帮我关心哪一个fd上的那一个事件
// 关键2.内核告诉用户：你关心的哪些fd上的哪些事件就绪了

// int epoll_create(int size);
// size参数忽略，但必须大于0
// 返回值为epoll的fd，不用时记得close
// 作用为创建epoll模型

// int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 解决关键1.用户告诉内核：你要帮我关心哪一个fd上的那一个事件
// 第一个参数为epoll的fd，由epoll_create(int size)返回
// 第二个参数op有三种模式：EPOLL_CTL_ADD(添加) EPOLL_CTL_MOD(修改) EPOLL_CTL_DEL(删除)
// 底层的红黑树保证其效率优秀
// 第二个参数即为哪一个fd
// 第三个参数event表示哪一个事件，结构体内容如下：
// struct epoll_event
// {
//     uint32_t events;   /* Epoll events */
//     epoll_data_t data; /* User data variable */
// };
// typedef union epoll_data
// {
//     void *ptr;
//     int fd;
//     uint32_t u32;
//     uint64_t u64;
// } epoll_data_t;

// int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
// 解决关键2.内核告诉用户：你关心的哪些fd上的哪些事件就绪了
// 第一个参数为epoll的fd，由epoll_create(int size)返回
// 第二个参数为输出型参数
// 第四个参数单位为毫秒，为-1时表示阻塞等待，为0时表示非阻塞

// 事件：
// EPOLLIN(表示对应的文件描述符可读)
// EPOLLOUT(表示对应的文件描述符可写)
// EPOLLPRI(表示对应的文件描述符有紧急数据可读)

// TCP的PSH标志位本质就是在操作系统层让sockfd的文件数据处于就绪状态

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include "sock.hpp"
#include "epoller.hpp"
#include "util.hpp"

class connection;
using cb_t = std::function<void (connection*)>;
using func_t = std::function<std::string (std::string&)>;

class connection
{
public:
    connection(){}
    ~connection(){}

    void init(cb_t recver, cb_t sender, cb_t excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }

public:
    // IO
    int _fd;
    std::string _inbuffer;
    std::string _outbuffer;

    // 回调函数
    cb_t _recver;
    cb_t _sender;
    cb_t _excepter;

    // 事件
    uint32_t _events;
};

class epoll_server
{
    const size_t N = 1024;
public:
    epoll_server(func_t func, uint16_t port = 8888)
    :_port(port)
    ,_func(func)
    ,_eventss(std::vector<epoll_event>(N))
    {}

    ~epoll_server()
    {
        _listen_sock.Close();
        _epoller.Close();
    }

    void init()
    {
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();
        _epoller.Create();
        add_conn(_listen_sock.GetSock(), EPOLLIN | EPOLLET);//ET模式倒闭程序员一次读/写完就绪事件
        std::cout << "初始化完成" << std::endl;
    }

    // 事件派发器
    void dispatch()
    {
        while(1)
        {
            loop_once();
        }
    }

    void loop_once()
    {
        int n = epoll_wait(_epoller.GetFd(), &_eventss[0], N, -1);
        for(size_t i = 0; i < N; ++i)
        {
            int fd = _eventss[i].data.fd;
            uint32_t events = _eventss[i].events;

            if((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);//将所有异常转化为recv和send异常
            if((events & EPOLLIN) && conn_is_exist(fd))
                _conns[fd]->_recver(_conns[fd]);
            if((events & EPOLLOUT) && conn_is_exist(fd))
                _conns[fd]->_sender(_conns[fd]);
        }
    }

    // 连接管理器
    void accepter(connection *conn)
    {
        std::cout << "新连接" << std::endl;
        do
        {
            std::string client_ip;
            uint16_t client_port;
            int err_code;
            int sock = _listen_sock.Accept(&client_ip, &client_port, &err_code);
            if(sock > 0)
            {
                add_conn(sock, EPOLLIN | EPOLLET);
                std::cout << "新连接添加成功，fd为" << sock << std::endl;
            }
            else
            {
                if(err_code == EAGAIN || err_code == EWOULDBLOCK)
                {
                    // 因非阻塞导致错误码被设置，说明事件处理完了，可结束循环
                    std::cout << "新连接完毕" << std::endl;
                    break;
                }
                else if(err_code == EINTR)
                {
                    // 因信号导致错误码被设置，不能保证事件处理完了，不可结束循环
                    continue;
                }
                else
                {
                    // 连接出错
                    perror("accept fail");
                    continue;
                }
            }
            
        } while (conn->_events & EPOLLET);//若为ET模式，须将就绪连接的事件处理完为止
        
    }

    void recver(connection *conn)
    {
        std::cout << "读取事件就绪" << std::endl;
        int flag = 0;//标志conn是否因异常处理了
        do
        {
            std::string buffer(4096, 0);
            int n = recv(conn->_fd, &buffer[0], buffer.size(), 0);
            if(n > 0)
            {
                conn->_inbuffer += buffer.substr(0, n);
                std::cout << "读取后conn->_inbuffer："<< conn->_inbuffer << std::endl;
            }
            else if(n == 0)
            {
                // 对方执行有序关闭后，recv的返回值为0
                // 因非阻塞导致n等于0，说明事件处理完了，可结束循环
                // 既然对方执行关闭了，不要忘了关闭fd
                std::cout << "fd:" << conn->_fd << "有序退出" << std::endl;
                conn->_excepter(conn);
                flag = 1;
                break;
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    // 因非阻塞导致错误码被设置，说明事件处理完了，可结束循环
                    std::cout << "就绪数据读取完毕" << std::endl;
                    break;
                }
                else if(errno == EINTR)
                {
                    // 因信号导致错误码被设置，不能保证事件处理完了，不可结束循环
                    continue;
                }
                else
                {
                    // 读取出错
                    perror("recv fail");
                    conn->_excepter(conn);
                    flag = 1;
                    break;
                }
            }
        } while (conn->_events & EPOLLET);//若为ET模式，须将就绪数据读取完为止
        
        if(flag == 0)
        {
            // 应用层协议处理
            // ...
            handle_request(conn);
            std::cout << "应用层协议处理后conn->_outbuffer：" << conn->_outbuffer << std::endl;

            // 一般面对写入的时候，第一次直接写入，没写完再交给epoll
            // 且读事件常设置，写事件按需设置
            if(!conn->_outbuffer.empty())
                conn->_sender(conn);
        }
    }

    void sender(connection *conn)
    {
        int flag = 0;//标志写入是否发生异常
        std::cout << "写入事件就绪" << std::endl;
        do
        {
            int n = send(conn->_fd, &conn->_outbuffer[0], conn->_outbuffer.size(), 0);
            if(n > 0)
            {
                std::cout << "写入：" << conn->_outbuffer << std::endl;
                conn->_outbuffer.erase(0, n);
                if(conn->_outbuffer.empty())
                {
                    std::cout << "写入事件处理完毕" << std::endl;
                    break;
                }   
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    std::cout << "写入事件处理完毕" << std::endl;
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    std::cout << "发送错误：" << strerror(errno) << std::endl;
                    conn->_excepter(conn);
                    flag = 1;
                    break;
                }
            }
        } while (conn->_events & EPOLLET);

        if(flag == 0)
        {
            if(conn->_outbuffer.empty())
                enable_read_write(conn, true, false);
            else
                enable_read_write(conn, true, true);
        }
    }

    void excepter(connection *conn)
    {
        std::cout << "已在epoll中移除fd：" << conn->_fd << std::endl;
        // 在epoll中移除fd
        _epoller.DelEvent(conn->_fd);
        // 移除conns中的元素
        _conns.erase(conn->_fd);
        // 关闭fd
        close(conn->_fd);
        // 释放conn，可用智能指针取代
        delete conn;
    }

    // 处理请求
    void handle_request(connection *conn)
    {
        // 获取完整报文
        // 分离报头
        // 反序列化
        // 业务处理
        // 序列化
        // 添加报头

        std::string response = _func(conn->_inbuffer);
        conn->_outbuffer += response;
    }

    // 添加连接
    void add_conn(int fd, uint32_t events)
    {
        // 若为ET模式，设置fd为非阻塞
        if(events & EPOLLET)
            util::set_non_block(fd);
        connection *conn = new connection();
        conn->_fd = fd;
        conn->_events = events;
        if(fd == _listen_sock.GetSock())
        {
            // _listen_sock的recver方法替换为accepter
            conn->init(std::bind(&epoll_server::accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn->init(std::bind(&epoll_server::recver, this, std::placeholders::_1),
                       std::bind(&epoll_server::sender, this, std::placeholders::_1),
                       std::bind(&epoll_server::excepter, this, std::placeholders::_1));
        }
        
        // 将conn管理起来
        _conns.insert(std::pair<int, connection*>(fd, conn));

        // 将要关心的事件写入内核
        _epoller.AddEvent(fd, events);
    }

    void enable_read_write(connection *conn, bool read_flag, bool write_flag)
    {
        uint32_t events = 0;
        if(read_flag == true)
            events |= EPOLLIN;
        if(write_flag == true)
            events |= EPOLLOUT;
        conn->_events = events | EPOLLET;
        _epoller.ModEvent(conn->_fd, conn->_events);
    }

    bool conn_is_exist(int fd)
    {
        return _conns.find(fd) != _conns.end();
    }
private:
    uint16_t _port;                               // 端口号
    sock _listen_sock;                            // 封装的socket类
    epoller _epoller;                             // 封装的epoll类
    std::vector<epoll_event> _eventss;            // 事件数组
    std::unordered_map<int, connection*> _conns;  // 连接哈希表
    func_t _func;                                 // 业务处理回调函数
};